﻿using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;

namespace SingleBlog.Core.Data
{
    using Microsoft.EntityFrameworkCore;
    using Services;
    using SettingModels;
    using System;
    using System.Linq;
    using System.Transactions;

    /// <summary>
    /// 提供数据初始化的功能。
    /// </summary>
    public interface IDataInitializer
    {
        /// <summary>
        /// 以异步的方式执行初始化的功能。
        /// </summary>
        /// <param name="param">自定义初始化所需要的对象。</param>
        Task InitializeAsync(dynamic param);
    }

    /// <summary>
    /// 表示默认的数据初始化器。在系统第一次安装时执行。
    /// </summary>
    /// <seealso cref="SingleBlog.Core.Data.IDataInitializer" />
    public class DefaultDataIntializer : IDataInitializer
    {
        private readonly DbContext _context;
        private readonly UserManager<SingleBlogUser> _userManager;
        private readonly RoleManager<SingleBlogRole> _roleManager;
        private readonly SettingService _settingService;
        private readonly CategoryService _categoryService;
        private readonly NavigationService _navigationService;
        public DefaultDataIntializer(
            DbContext context,
            UserManager<SingleBlogUser> userManager,
            RoleManager<SingleBlogRole> roleManager,
            SettingService settingService,
            CategoryService categoryService,
            NavigationService navigationService
            )
        {
            _context = context;
            _userManager = userManager;
            _roleManager = roleManager;
            _settingService = settingService;
            _categoryService = categoryService;
            _navigationService = navigationService;
        }

        /// <summary>
        /// 以异步的方式执行初始化的功能。
        /// </summary>
        /// <param name="param">该对象应该是一个 model，用于初始化管理员帐号和密码。</param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        public async Task InitializeAsync(dynamic param)
        {
            await _context.Database.EnsureDeletedAsync();
            await _context.Database.EnsureCreatedAsync();
            using (var tran = _context.Database.BeginTransaction())
            {
                try
                {

                    _settingService.SaveSetting(GeneralSettingModel.Default());
                    _settingService.SaveSetting(new SingleBlogInfoSettingModel());

                    await _categoryService.SaveCategoryAsync(new Category { Name = Category.DEFAULT_NAME, IsSystem = true });
                    await _navigationService.SaveNavigationAsync(new Navigation { Name = "首页", Url = "/", Enabled = true, OrderNo = 0 });

                    var roleResult = await _roleManager.CreateAsync(new SingleBlogRole { Name = SingleBlogRole.ADMINISTRATOR, NormalizedName = SingleBlogRole.ADMINISTRATOR_NAME });
                    if (!roleResult.Succeeded)
                    {
                        throw new DataInitializeException(roleResult.Errors.Select(m => m.Description).Join(Environment.NewLine));
                    }

                    var user = new SingleBlogUser { Email = param.Email, UserName = param.Account };
                    IdentityResult userResult = await _userManager.CreateAsync(user, param.Password);
                    if (!userResult.Succeeded)
                    {
                        throw new DataInitializeException(userResult.Errors.Select(m => m.Description).Join(Environment.NewLine));
                    }

                    var addRoleResult = await _userManager.AddToRoleAsync(user, SingleBlogRole.ADMINISTRATOR);
                    if (!addRoleResult.Succeeded)
                    {
                        throw new DataInitializeException(addRoleResult.Errors.Select(m=>m.Description).Join(Environment.NewLine));
                    }
                    tran.Commit();
                }
                catch
                {
                    tran.Rollback();
                    throw;
                }
            }
        }
    }

    /// <summary>
    /// 尝试在数据初始化时引发的异常。
    /// </summary>
    /// <seealso cref="System.InvalidOperationException" />
    public class DataInitializeException : InvalidOperationException
    {
        static string Message(string message) => "数据初始化出现异常，可能是下列原因造成的：{0}{1}{0}你可以尝试解决或重新进行初始化。".StringFormat(Environment.NewLine, message);
        /// <summary>
        /// Initializes a new instance of the <see cref="DataInitializeException"/> class.
        /// </summary>
        /// <param name="message">The message that describes the error.</param>
        public DataInitializeException(string message) : base(Message(message))
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DataInitializeException"/> class.
        /// </summary>
        /// <param name="message">The error message that explains the reason for the exception.</param>
        /// <param name="innerException">The exception that is the cause of the current exception. If the innerException parameter is not a null reference (Nothing in Visual Basic), the current exception is raised in a catch block that handles the inner exception.</param>
        public DataInitializeException(string message, Exception innerException) : base(Message(message), innerException)
        {
        }
    }
}
